/*============================================================================
  File:      ParamHandler.cpp
 
  Summary:   Implementation of DMHPARAMHANDLER class that automatically 
			 handles parameter handling.
 
  Date:		 June 30, 2004
------------------------------------------------------------------------------
  This file is part of the Microsoft SQL Server Code Samples.
 
  Copyright (C) 2003 Microsoft Corporation.  All rights reserved.
 
This source code is intended only as a supplement to Microsoft
Development Tools and/or on-line documentation.  See these other
materials for detailed information regarding Microsoft code samples.
 
THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
============================================================================*/

#include "stdafx.h"
#include "ParamHandler.h"
#include <atlbase.h>
#include <atlcom.h>
#include "DmhLocalization.h"
	
DBTYPEENUM	Dbtypeenum(const WCHAR* szType)
{
	// Convert from the string name to the enumeration. 

	if (wcscmp(szType, L"DBTYPE_I2") == 0)
	{
		return DBTYPE_I2;
	}
	else if (wcscmp(szType, L"DBTYPE_I4") == 0)
	{
		return DBTYPE_I4;
	}
	else if (wcscmp(szType, L"DBTYPE_I8") == 0)
	{
		return DBTYPE_I8;
	}
	else if (wcscmp(szType, L"DBTYPE_R4") == 0)
	{
		return DBTYPE_R4;
	}
	else if (wcscmp(szType, L"DBTYPE_R8") == 0)
	{
		return DBTYPE_R8;
	}
	else if (wcscmp(szType, L"DBTYPE_BOOL") == 0)
	{
		return DBTYPE_BOOL;
	}
	else if (wcscmp(szType, L"DBTYPE_UI2") == 0)
	{
		return DBTYPE_UI2;
	}
	else if (wcscmp(szType, L"DBTYPE_UI4") == 0)
	{
		return DBTYPE_UI4;
	}
	else if (wcscmp(szType, L"DBTYPE_UI8") == 0)
	{
		return DBTYPE_UI8;
	}
	else 
	{
		return DBTYPE_NULL;
	}
}

const WCHAR* SzType(DBTYPEENUM dbtypeenum)
{
	// Convert from the enumeration to the string name

	switch (dbtypeenum)
	{
		case DBTYPE_I2:
			return L"DBTYPE_I2";

		case DBTYPE_I4:
			return L"DBTYPE_I4";

		case DBTYPE_I8:
			return L"DBTYPE_I8";

		case DBTYPE_R4:
			return L"DBTYPE_R4";

		case DBTYPE_R8:
			return L"DBTYPE_R8";

		case DBTYPE_BOOL:
			return L"DBTYPE_BOOL";

		case DBTYPE_UI2:
			return L"DBTYPE_UI2";

		case DBTYPE_UI4:
			return L"DBTYPE_UI4";

		case DBTYPE_UI8:
			return L"DBTYPE_UI8";

		default: 
			return L"DBTYPE_EMPTY";

	}
}

// Functions to extract the parameter values for simple types. You can
// use these in IDMAlgorithm::InsertCases() to extract the values passed
// in. These simple implementations traverse all of the parameters on 
// each call. If szParameter is not included in the provided rgszParameter,
// the list _rgdmhparam is traversed and the default value is returned. The
// functions return E_FAIL if szParameter is not found.

HRESULT	DMHPARAMHANDLER::GetVariant(IDMContextServices* pidmcontextservices,
								   const WCHAR*	szParameter,	// Name of desired parameter
								   VARIANT&		variant,		// Output value
								   ULONG		cParameter,		
								   WCHAR**		rgszParameter,
								   DMVariantPtr	rgdmvariantptrValue[])
{
	CComPtr<IDMVariantPtrHandler> spidmvarianthandler;

	HRESULT hr	= pidmcontextservices->GetVariantHandler(&spidmvarianthandler);

	RETURN_ON_FAIL(hr);

	// Check for a match in the parameter list

	for (UINT iParameter = 0; iParameter < cParameter; iParameter++)
	{
		// Using _wcsicmp - parameter names are case insensitive

		if (_wcsicmp(szParameter, rgszParameter[iParameter]) == 0)
		{
			// Match: set the input VARIANT value using the supplied parameter.

			hr = spidmvarianthandler->CopyHandleToVariant(rgdmvariantptrValue[iParameter], 
														  &variant);
			return hr;
		}
	}
	 
	// The parameter was not supplied... find the default value

	for (UINT idmhparam = 0; idmhparam < _cdmhparam; idmhparam++)
	{
		// Using _wcsicmp - parameter names are case insensitive

		if (_wcsicmp(szParameter, _rgdmhparam[idmhparam]._szName) == 0)
		{
			// Match: parse the default value 
			
			WCHAR*	szDefault	= const_cast<WCHAR*>(_rgdmhparam[idmhparam]._szDefault);
			
			hr = ParseParameterValue(idmhparam, szDefault, variant);
		
			RETURN_ON_FAIL(hr);

			return S_OK;
		}
	}

	// The given parameter was not declared anywhere!

	// Raise the appropriate error

	// First get the appropriate description
	// Get the locale from the context-services interface 

	LCID lcid;

	hr = pidmcontextservices->GetContextLocaleID(&lcid);

	RETURN_ON_FAIL(hr);

//	WCHAR szError[201];

//	hr = ::LoadStringFromID(IDS_ERROR_UNDECLARED_PARAMETER, lcid, szError, 200);

	RETURN_ON_FAIL(hr);

	//RaiseError(S_UNDECLARED_PARAMETER, szError);

	//return S_UNDECLARED_PARAMETER;

	return E_FAIL;
}
       

HRESULT DMHPARAMHANDLER::GetParameterValue(IDMContextServices*	pidmcontextservices,
										  double&		dblValue,		// Output value
										  const WCHAR*	szParameter,	// Name of desired parameter
										  ULONG			cParameter,		
										  WCHAR**		rgszParameter,
										  DMVariantPtr  rgdmvariantptrValue[])
{
	VARIANT variantFrom;

	::VariantInit(&variantFrom);

	HRESULT hr = GetVariant(pidmcontextservices,
							szParameter,	
							variantFrom,
							cParameter,		
							rgszParameter,
							rgdmvariantptrValue);

	RETURN_ON_FAIL(hr);

	// Change to type double: because these are all numeric types, no memory
	// will be allocated

	VARIANT variantTo;

	::VariantInit(&variantTo);

	hr = ::VariantChangeType(&variantTo, &variantFrom, 0, VT_R8);

	RETURN_ON_FAIL(hr);

	dblValue = variantTo.dblVal;

	return S_OK;
} 

HRESULT DMHPARAMHANDLER::GetParameterValue(IDMContextServices*	pidmcontextservices,
										  LONG&			lValue,			// Output value
										  const WCHAR*	szParameter,	// Name of desired parameter
										  ULONG			cParameter,		
										  WCHAR**		rgszParameter,
										  DMVariantPtr  rgdmvariantptrValue[])
{
	VARIANT variantFrom;

	::VariantInit(&variantFrom);

	HRESULT hr = GetVariant(pidmcontextservices,
							szParameter,	
							variantFrom,
							cParameter,		
							rgszParameter,
							rgdmvariantptrValue);

	RETURN_ON_FAIL(hr);

	// Change to type LONG: because these are all numeric types, no memory
	// will be allocated

	VARIANT variantTo;

	::VariantInit(&variantTo);

	hr = ::VariantChangeType(&variantTo, &variantFrom, 0, VT_I4);

	RETURN_ON_FAIL(hr);

	lValue = variantTo.lVal;

	return S_OK;
}

HRESULT DMHPARAMHANDLER::GetParameterValue(IDMContextServices*	pidmcontextservices,
										  bool&			bValue,			// Output value
										  const	WCHAR*	szParameter,	// Name of desired parameter
										  ULONG			cParameter,		
										  WCHAR**		rgszParameter,
										  DMVariantPtr  rgdmvariantptrValue[])
{
	VARIANT variantFrom;

	::VariantInit(&variantFrom);

	HRESULT hr = GetVariant(pidmcontextservices,
							szParameter,	
							variantFrom,
							cParameter,		
							rgszParameter,
							rgdmvariantptrValue);

	RETURN_ON_FAIL(hr);

	// Change to type BOOL: because these are all numeric types, no memory
	// will be allocated

	VARIANT variantTo;

	::VariantInit(&variantTo);

	hr = ::VariantChangeType(&variantTo, &variantFrom, 0, VT_BOOL);

	RETURN_ON_FAIL(hr);

	bValue = (variantTo.boolVal == TRUE) ? true : false;

	return S_OK;
}

HRESULT DMHPARAMHANDLER::CreateDMString(IDMContextServices*	pidmcontextservices,
									   const WCHAR*			szToCopy,
									   DMString*			pdmstring)
{
	// Helper function for creating strings

	CComPtr<IDMStringHandler> spidmstringhandler;	// Smart pointer for a string handler

	HRESULT hr	= pidmcontextservices->GetStringHandler(&spidmstringhandler);

	RETURN_ON_FAIL(hr);

	// CopyBufferToHandler does not write on the input buffer, so we 
	// can safely cast to a non-const.

	WCHAR* szToCopyNonConst = const_cast<WCHAR*>(szToCopy);

	return spidmstringhandler->CopyBufferToHandle(pdmstring, szToCopyNonConst, (UINT)wcslen(szToCopy));
}

////
//
// The following functions are implementations of the IDMAlgorithmMetadata parameter-handing 
// functions that will be called directly by the corresponding static members of the algorithm
// class.
//
////
	
HRESULT DMHPARAMHANDLER::GetNumParameters(IDMContextServices* in_pContext, ULONG* pcParameter)
{
	*pcParameter = _cdmhparam;
	return S_OK;
}

HRESULT  DMHPARAMHANDLER::GetParameterName(IDMContextServices*	in_pContext,
										 ULONG					in_iParameter,						
										 DMString				*io_pstrParameterName)
{
	return CreateDMString(in_pContext, _rgdmhparam[in_iParameter]._szName, io_pstrParameterName);
}

HRESULT  DMHPARAMHANDLER::GetParameterType(IDMContextServices*	in_pContext,
										  ULONG					in_iParameter,
										  DMString*				io_pstrParameterType)
{
	return CreateDMString(in_pContext, 
						  ::SzType(_rgdmhparam[in_iParameter]._dbtypeenum), 
						  io_pstrParameterType);
}

HRESULT  DMHPARAMHANDLER::GetParameterIsRequired(IDMContextServices*	in_pContext,
									   ULONG				in_iParameter,
									   BOOL*				out_pfIsRequired)
{
	*out_pfIsRequired = _rgdmhparam[in_iParameter]._bRequired;

	return S_OK;
}

HRESULT  DMHPARAMHANDLER::GetParameterIsExposed(IDMContextServices*	in_pContext, ULONG in_iParameter, BOOL* out_pfIsExposed)
{
	*out_pfIsExposed = _rgdmhparam[in_iParameter]._bExposed;

	return S_OK;
}

HRESULT  DMHPARAMHANDLER::GetParameterFlags(IDMContextServices*	in_pContext,
										   ULONG				in_iParameter,
										   ULONG*				out_pulParameterFlags)
{
	*out_pulParameterFlags = _rgdmhparam[in_iParameter]._ulFlags;

	return S_OK;
}

HRESULT  DMHPARAMHANDLER::GetParameterDescription(IDMContextServices*	in_pContext,
												 ULONG					in_iParameter,
												 ULONG					in_LocaleID,
												 DMString*				io_pstrDescription)
{
	// A little confusing here: the locale ID passed in as in_LocaleID should be used
	// UNLESS it is equal to zero, in which case the locale ID should be obtained
	// from in_pContext.

	LCID lcid;

	if (in_LocaleID == 0)
	{
		HRESULT hr = in_pContext->GetContextLocaleID(&lcid);

		RETURN_ON_FAIL(hr);
	}
	else
	{
		lcid = in_LocaleID;
	}


	// The description should be a localized string

	WCHAR szDescription[MAX_DESCRIPTION_LENGTH + 1]; 

	int	iIDStrDescription = _rgdmhparam[in_iParameter]._iIDStrDescription;

	HRESULT hr = LoadStringFromID(iIDStrDescription, 
								  lcid, 
								  szDescription, 
								  MAX_DESCRIPTION_LENGTH);

	RETURN_ON_FAIL(hr);
	
	return CreateDMString(in_pContext, szDescription, io_pstrDescription);
}

HRESULT  DMHPARAMHANDLER::GetParameterDefaultValue(IDMContextServices*	in_pContext,
												  ULONG					in_iParameter,
												  DMString*				io_pstrDefaultValue)
{
	return CreateDMString(in_pContext, _rgdmhparam[in_iParameter]._szDefault, io_pstrDefaultValue);
}

HRESULT  DMHPARAMHANDLER::GetParameterValueEnumeration(IDMContextServices*	in_pContext,
													  ULONG					in_iParameter,
													  DMString*				io_pstrValueEnumeration)
{
	return CreateDMString(in_pContext, _rgdmhparam[in_iParameter]._szValueEnum, io_pstrValueEnumeration);
}

HRESULT  DMHPARAMHANDLER::ParseParameterValue(ULONG			in_iParameter,
											 const WCHAR*	szValue,
                                             VARIANT&		variant)
{
	// Parsing function that uses VARIANT and WCHAR* instead of a DMVariantPtr
	// and DMString

	::VariantInit(&variant);

	// Check the type 

	DBTYPEENUM dbtypeenum = _rgdmhparam[in_iParameter]._dbtypeenum;

	switch (dbtypeenum)
	{
		case DBTYPE_I2:
		{
			variant.vt		= VT_I2;
			variant.iVal	= _wtoi(szValue);
			break;
		}
		case DBTYPE_I4:
		{
			variant.vt		= VT_I4;
			variant.lVal	= _wtoi(szValue);
			break;
		}
		case DBTYPE_I8:
		{
			variant.vt		= VT_I8;
			variant.llVal	= _wtoi64(szValue);
			break;
		}
		case DBTYPE_R4:
		{
			variant.vt		= VT_R4;
			variant.fltVal	= (float) _wtof(szValue);
			break;
		}
		case DBTYPE_R8:
		{
			variant.vt		= VT_R8;
			variant.dblVal	= _wtof(szValue);
			break;
		}
		case DBTYPE_BOOL:
		{
			variant.vt		= VT_BOOL;
		
			// If it converts to an integer that is not
			//	zero, the value is TRUE.
			// Else if it is equal to "true" (without regard to case), 
			//	then the value is TRUE
			// Else the value is FALSE

			int i = _wtoi(szValue);

			if (i == 0)
			{
				// Either it really is a zero, or it doesn't convert to 
				// an integer

				if (_wcsnicmp(szValue, L"true", 4) == 0)
				{
					variant.boolVal = TRUE;
				}
				else
				{
					variant.boolVal = FALSE;
				}
			}
			else 
			{
				variant.boolVal = TRUE;
			}
			break;
		}
		case DBTYPE_UI2:
		{
			variant.vt		= VT_UI2;
			variant.uiVal	= _wtoi(szValue);
			break;
		}
		case DBTYPE_UI4:
		{
			variant.vt		= VT_I4;
			variant.ulVal	= _wtoi(szValue);
			break;
		}
		case DBTYPE_UI8:
		{
			variant.vt		= VT_I8;
			variant.ullVal	= _wtoi64(szValue);
		}
		default: 
		{
			return S_FALSE;
		}
	}
	
	return S_OK;
}

HRESULT  DMHPARAMHANDLER::ParseParameterValue(IDMContextServices*	in_pContext,
											 ULONG					in_iParameter,
											 DMString*				in_pstrParameterStrValue,
											 DMVariantPtr			io_pParameterMappedValue)
{
	// First, get the string handler

	CComPtr<IDMStringHandler> spidmstringhandler;	// Smart pointer for a string handler

	HRESULT hr	= in_pContext->GetStringHandler(&spidmstringhandler);

	RETURN_ON_FAIL(hr);

	const WCHAR*	szValue;
	UINT			cchValue;

	spidmstringhandler->GetConstStringFromHandle(in_pstrParameterStrValue, 
												 &szValue,
												 &cchValue);

	
	VARIANT variant;

	hr = ParseParameterValue(in_iParameter, szValue, variant);

	// Get the variant handler

	CComPtr<IDMVariantPtrHandler> spidmvarianthandler;	// Smart pointer for a variant handler

	hr	= in_pContext->GetVariantHandler(&spidmvarianthandler);

	RETURN_ON_FAIL(hr);

	return spidmvarianthandler->CopyVariantToHandle(io_pParameterMappedValue, &variant);
}